// Fill out your copyright notice in the Description page of Project Settings.


#include "Layers/MotionSubtitlesNiagaraLayer.h"
#include "MotionSubtitlesActor.h"
#include "Layers/MotionSubtitlesTextLayer.h"
#include "NiagaraComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/TextureRenderTarget2D.h"


UMotionSubtitlesNiagaraLayer::UMotionSubtitlesNiagaraLayer()
{
	PrimaryComponentTick.bCanEverTick = true;

	struct FConstructorStatics
	{
		ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialFinder;
		FConstructorStatics(): MaterialFinder(TEXT("/Script/Engine.Material'/MotionSubtitles/Materials/NiagaraLayer/M_StylizedText_TextOnly.M_StylizedText_TextOnly'"))
		{
		}
	};

	static FConstructorStatics ConstructorStatics;
	if (ConstructorStatics.MaterialFinder.Succeeded())
	{
		TextMaterialOverride = ConstructorStatics.MaterialFinder.Object;
	}

	EmitterMaskCaptureComponent2D = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("EmitterMaskCaptureComponent2D"));
	EmitterMaskCaptureComponent2D->SetupAttachment(this);
	EmitterMaskCaptureComponent2D->ProjectionType = ECameraProjectionMode::Orthographic;
	EmitterMaskCaptureComponent2D->SetRelativeLocation(FVector(-1000, 0, 0));
	EmitterMaskCaptureComponent2D->bCaptureEveryFrame = true;
	EmitterMaskCaptureComponent2D->bAlwaysPersistRenderingState = false;
	EmitterMaskCaptureComponent2D->ShowFlags.Atmosphere = false;
	EmitterMaskCaptureComponent2D->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList;

	LayerMask = ELayerMask::Layer0;
}

void UMotionSubtitlesNiagaraLayer::Init(const FUint32Vector2& ViewSize)
{
	Super::Init(ViewSize);

	EmitterMask = NewObject<UTextureRenderTarget2D>(this);
	EmitterMask->RenderTargetFormat = RTF_RGBA16f;
	EmitterMask->ClearColor = FLinearColor::Black;
	EmitterMask->bAutoGenerateMips = false;
	EmitterMask->bCanCreateUAV = false;
	EmitterMask->InitAutoFormat(ViewSize.X, ViewSize.Y);
	EmitterMask->UpdateResourceImmediate(true);

	if (EmitterMaskCaptureComponent2D != nullptr && EmitterMask != nullptr)
	{
		EmitterMaskCaptureComponent2D->TextureTarget = EmitterMask;
		EmitterMaskCaptureComponent2D->OrthoWidth = EmitterMask->SizeX;
	}
}

void UMotionSubtitlesNiagaraLayer::Show(const FSubtitleParams& Params)
{
	Super::Show(Params);

	NiagaraComponent = NewObject<UNiagaraComponent>(this);
	NiagaraComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);
	NiagaraComponent->RegisterComponent();
	NiagaraComponent->bVisibleInSceneCaptureOnly = true;
	NiagaraComponent->bCastStaticShadow = false;
	NiagaraComponent->bCastDynamicShadow = false;
	NiagaraComponent->SetAsset(FXAsset);
	NiagaraComponent->SetVariableTexture(TEXT("EmitterMask"),EmitterMask);
	NiagaraComponent->SetVariableVec2(TEXT("MaskSize"),FVector2d(1920,1080)*0.5);
	
	SceneCaptureComponent2D->ShowOnlyComponent(NiagaraComponent);
}

void UMotionSubtitlesNiagaraLayer::ShowDeferred(const FSubtitleParams& Params)
{
	if (!DuplicateTextMeshes(TextPlanes))
	{
		return;
	}
	ReplaceTextMaterials(TextPlanes);

	for (auto TextPlane : TextPlanes)
	{
		EmitterMaskCaptureComponent2D->ShowOnlyComponent(TextPlane.Get());
	}
}

void UMotionSubtitlesNiagaraLayer::Hide()
{
	Super::Hide();

	NiagaraComponent->Deactivate();
	SceneCaptureComponent2D->RemoveShowOnlyComponent(NiagaraComponent);

	for (auto TextPlane : TextPlanes)
	{
		EmitterMaskCaptureComponent2D->RemoveShowOnlyComponent(TextPlane.Get());
	}
	TextPlanes.Empty();
}

void UMotionSubtitlesNiagaraLayer::Exit()
{
	Super::Exit();
}

void UMotionSubtitlesNiagaraLayer::DoScale(const EAnimTarget AnimTarget, const int32 ElementId, const float Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		SetRelativeScale3D(OriginScale * Value);
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			Element->SetRelativeScale3D(TextOriginScales[ElementId] * Value);
		}
	}
}

void UMotionSubtitlesNiagaraLayer::DoTranslate(const EAnimTarget AnimTarget, const int32 ElementId, const FVector Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		for (int32 i = 0; i < TextPlanes.Num(); i++)
		{
			TextPlanes[i]->SetRelativeLocation(TextOriginLocations[i] + Value);
		}
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			Element->SetRelativeLocation(TextOriginLocations[ElementId] + Value);
		}
	}
}

void UMotionSubtitlesNiagaraLayer::DoFade(const EAnimTarget AnimTarget, const int32 ElementId, const float Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		for (int32 i = 0; i < TextPlanes.Num(); i++)
		{
			UMaterialInterface* Material = TextPlanes[i]->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("AlphaFactor"), Value);
			}
		}
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			UMaterialInterface* Material = Element->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("AlphaFactor"), Value);
			}
		}
	}
}

void UMotionSubtitlesNiagaraLayer::DoDissolve(const EAnimTarget AnimTarget, const int32 ElementId, const float Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		for (const auto Element : TextPlanes)
		{
			UMaterialInterface* Material = Element->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("Dissolve"), Value);
			}
		}
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			UMaterialInterface* Material = Element->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("Dissolve"), Value);
			}
		}
	}
}

void UMotionSubtitlesNiagaraLayer::CaptureStatus()
{
	Super::CaptureStatus();

	TextOriginLocations.Empty();
	TextOriginScales.Empty();
	for (int32 i = 0; i < TextPlanes.Num(); i++)
	{
		// 记录文字初始相对位置
		TextOriginLocations.Add(TextPlanes[i]->GetRelativeLocation());
		TextOriginScales.Add(TextPlanes[i]->GetRelativeScale3D());
	}
}

int32 UMotionSubtitlesNiagaraLayer::ElementCount()
{
	return TextPlanes.Num();
}

bool UMotionSubtitlesNiagaraLayer::DuplicateTextMeshes(TArray<TObjectPtr<UStaticMeshComponent>>& TextMeshes)
{
	const AMotionSubtitlesActor* SubtitlesActor = Cast<AMotionSubtitlesActor>(GetOwner());
	if (!IsValid(SubtitlesActor))
	{
		return false;
	}

	auto Layers = SubtitlesActor->GetLayers();
	for (const auto Layer : Layers)
	{
		if (Layer->GetClass() == UMotionSubtitlesTextLayer::StaticClass())
		{
			const auto TextLayer = Cast<UMotionSubtitlesTextLayer>(Layer);
			for (const auto TextPlane : TextLayer->TextPlanes)
			{
				UObject* Obj = StaticDuplicateObject(TextPlane, this);
				UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(Obj);
				StaticMeshComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);
				StaticMeshComponent->RegisterComponent();
				StaticMeshComponent->bCastStaticShadow = false;
				StaticMeshComponent->bCastDynamicShadow = false;
				StaticMeshComponent->bVisibleInSceneCaptureOnly = true;
				StaticMeshComponent->SetHiddenInSceneCapture(false);
				TextMeshes.Add(StaticMeshComponent);
			}
		}
	}
	return true;
}

bool UMotionSubtitlesNiagaraLayer::ReplaceTextMaterials(TArray<TObjectPtr<UStaticMeshComponent>>& TextMeshes)
{
	for (const auto TextPlane : TextMeshes)
	{
		const UMaterialInterface* OldMaterial = TextPlane->GetMaterial(0);
		FHashedMaterialParameterInfo Info(TEXT("TextMap"));

		UTexture* OutValue;
		OldMaterial->GetTextureParameterValue(Info, OutValue);
		if (OutValue == nullptr)
		{
			continue;
		}

		UMaterialInstanceDynamic* MaterialInstance = UMaterialInstanceDynamic::Create(TextMaterialOverride, this);
		MaterialInstance->SetTextureParameterValue(TEXT("TextMap"), OutValue);

		TextPlane->SetMaterial(0, MaterialInstance);
	}
	return true;
}
